#include <stdio.h>

#include "World Module.h"
#include "World Includes.h"
#include "Engine\Engine.h"
#include "Item\ItemFactory.h"
#include "Item\Items.h"

class Engine;


void _World::Collide(Entity * E, bool isPlayer)
{
	float H, W = 0;
	RetVal collisionData;
	E->CalcBBDimensions(H,W);
	float X = E->getPos().x; 
	float Y = E->getPos().y * -1;
	bool Collides[DIR_SIZE] = {false};

	pointQuad EntityRect (X, Y , W, H);
	pointQuad headRect (X, Y - 1	,	 W, 1);	//1 Below and Center
	pointQuad footRect (X, Y + H + 1,	 W, 1);	//1 Below and Center
	pointQuad leftRect (X - 1		, Y+1, 0, H-2);
	pointQuad rightRect(X + W +1	, Y+1, 0, H-2);

	collisionData.Add(dataBlockCollide(EntityRect));						//Fills the data in from  temp value;
	Collides[DIR_UP]	= boolBlockCollide(headRect);
	Collides[DIR_DOWN]	= boolBlockCollide(footRect);
	Collides[DIR_LEFT]  = boolBlockCollide(leftRect);
	Collides[DIR_RIGHT] = boolBlockCollide(rightRect);

	if(E->CheckCollision((void*)&collisionData,Collides) && countToTen < 10)
	{
		bool secondary = false;
		for(int i = LOCAL_PLAYER+1; i < MAX_PLAYERS; ++i) {
			if(WORLD_MODULE::Ins()->Players[i])
				if(WORLD_MODULE::Ins()->Players[i] == E)
					secondary = true;
		}
		countToTen++;

		if(isPlayer) 
		{
			if(!secondary)		PlayerUpdate();
			else				E->UpdateSecondaryEntity();
		}
		//else					EnemyUpdate();
	}
	countToTen = 0;

	ProjectileCollide(E,EntityRect);
};
#define projList (TemplateVectorList<Item_Projectile>*)
int const _World::ProjectileUpdate() {

	void * List = Factory::get().GetProjectileList();
	if(!List) return FAIL_MEMORY_COPY;

	//=====DECLARATIONS
	Quad				boundRect;
	RetVal				collisionData;	//Info about whether or not the toCollide hit
	Vector3D			TPos;
	Item_Projectile*	toCollide;

	//Moved get collision list outside of the loop so that it grabs a large area for
	// all the projectiles instead of getting a new list for each projectile

	GetCollisionList(Players[LOCAL_PLAYER]->getPos().x,Players[LOCAL_PLAYER]->getPos().y,PROJECTILE_COLLIDE_RADIUS);
	//=====PROCESSING
	for(int i = 0; i < (projList(List))->size(); i++)
	{
		toCollide = &(projList(List))->get(i);

		// NEW PROJECTILE COLLIDE CODE
		//if(toCollide is marked for deletion, skip to uncreate and removeobjectbyref
		// the way that it is marked for deletion is determined in ProjectileCollide
		//else do main logic

		if(toCollide->itemType != ITEM_NULL) 
		{
			//=====Load info for comparing against			
			float scaleX = 10 * toCollide->getScale().x;
			float scaleY = 10 * toCollide->getScale().y; 

			TPos				= toCollide->getPos();
			boundRect.x			= short(TPos.x - 0.5*scaleX);
			boundRect.y			= short(-1*(TPos.y));
			boundRect.width		= short(scaleX);
			boundRect.height	= short(scaleY);

			collisionData.clear();
			collisionData.Add(dataBlockCollide(boundRect.convertToFloats()));

			//=====Process the info
			if(collisionData.y || collisionData.x)
			{

				bool destroyBlock = false;;
				int iconID = GetAssetArtID(((Block_Info*)collisionData.xObj));
				int ID2 = AllAssets.getByIndex(iconID).ID;
				if(toCollide->collideEffect != Item_Projectile::SPELL_PLACEBLOCK) 
				{
					if(((Block_Info*)collisionData.xObj)->Data->GetTrait_s() != BLOCK_TRAIT_UNBREAKABLE)
						if(toCollide->originFlag != P_TYPE_ENEMY) {
							if(iconID > NULL_ASSET_INDEX) {
								iconID = NULL_ASSET_INDEX+1;
								ID2 = AllAssets.getByIndex(iconID).ID;
							}
							destroyBlock = Players[LOCAL_PLAYER]->projectileHasHit(toCollide,ID2,AllIcons[iconID],worldBlockNames[iconID]);
						}
				}
				else 
				{
					bool place = true;
					Entity* E;
					for(int i = 0; i < Engine::Ins()->game.getNumPlayers(); i++)
						if((toCollide->getPos().x < Players[0]->getPos().x + BLOCK_TEXTURE_SIZE * 1.2f &&
							toCollide->getPos().x > Players[0]->getPos().x - BLOCK_TEXTURE_SIZE * 1.2f &&
							toCollide->getPos().y < Players[0]->getPos().y + BLOCK_TEXTURE_SIZE * 1.2f &&
							toCollide->getPos().y > Players[0]->getPos().y - BLOCK_TEXTURE_SIZE * 1.2f))
							place = false;

					for(int i = 1; i < AllEntities->size(); i++)
					{
						E = AllEntities->getByIndex(i);
						if((toCollide->getPos().x < E->getPos().x + BLOCK_TEXTURE_SIZE * 1.2f &&
							toCollide->getPos().x > E->getPos().x - BLOCK_TEXTURE_SIZE * 1.2f &&
							toCollide->getPos().y < E->getPos().y + BLOCK_TEXTURE_SIZE * 1.2f &&
							toCollide->getPos().y > E->getPos().y - BLOCK_TEXTURE_SIZE * 1.2f))
							place = false;
					}


					if(place && Players[LOCAL_PLAYER]->removeBlockFromInv(worldBlockNames[iconID]))
						this->placeBlock(toCollide,collisionData);
					destroyBlock = false;
				}
				//TODO, MAKE THIS WORK FOR ALL ENTITIES

				if(destroyBlock)
				{
					WorldChange* wc = new WorldChange(collisionData.xP,collisionData.yP,(DEFAULT_AIR_BLOCK/*|(AllAssets.getByIndex(AIR_ASSET_INDEX).ID)*/));
					AddChange(wc);
					Engine::Ins()->game.m_network.MakeServerRequest(NETWORK_UPDATE_WORLD_BLOCK, wc, sizeof(WorldChange)); 
				}
				Factory::get().uncreate((Item_Base*)(toCollide),ITEM_PROJECTILE);
				Engine::Ins()->dlHandler.RemoveObjectByRef(DRAW_ENTITIES,(BaseObject*)toCollide);

			}//End if
		}//End if
	}//End for
	return SUCCEED;
}
#undef rpojlist
bool _World::placeBlock(Item_Projectile* collider, RetVal collisionData)
{
	bool UP		(collider->vector.y?1:0);
	bool LEFT	(collider->getPos().x < (collisionData.xP - BLOCK_TEXTURE_SIZE/2));
	bool xCenter(collider->getPos().x > (collisionData.xP - BLOCK_TEXTURE_SIZE/2)
		&& collider->getPos().x < (collisionData.xP + BLOCK_TEXTURE_SIZE/2));
	bool yCenter(collider->getPos().y > (collisionData.yP - BLOCK_TEXTURE_SIZE/2)
		&& collider->getPos().y < (collisionData.yP + BLOCK_TEXTURE_SIZE/2));
	int xPos = collisionData.xP;
	int yPos = collisionData.yP;

	if(!yCenter)
	{
		if (UP)		yPos = collisionData.yP + BLOCK_TEXTURE_SIZE;
		else		yPos = collisionData.yP - BLOCK_TEXTURE_SIZE;
	}
	if(!xCenter)
	{
		if(LEFT)	xPos = collisionData.xP - BLOCK_TEXTURE_SIZE;
		else		xPos = collisionData.xP + BLOCK_TEXTURE_SIZE;
	}

	int blockType = determineBlockTraitByName(collider->itemName);
	int assetType = determineBlockGraphicByName(collider->itemName);

	WorldChange* wc = new WorldChange(xPos,yPos,(blockType|(AllAssets.getByIndex(assetType).ID)));
	AddChange(wc);
	Engine::Ins()->game.m_network.MakeServerRequest(NETWORK_UPDATE_WORLD_BLOCK, wc, sizeof(WorldChange)); 
	Factory::get().uncreate(collider,ITEM_PROJECTILE);
	return false;
};


int	 _World::determineBlockTraitByName(char* Name)
{
	if(Name == "Foam")				return DEFAULT_DIRT_BLOCK;
	else if(Name == "Turf")			return DEFAULT_GRASS_BLOCK;
	else if(Name == "Scrap")		return DEFAULT_ROCK_BLOCK;
	else if(Name == "Fiber")		return DEFAULT_TREE_BLOCK;
	else if(Name == "Algae Wire")	return DEFAULT_LEAF_BLOCK;
	else if(Name == "Inorite")		return DEFAULT_ORE1_BLOCK;
	return DEFAULT_LEAF_BLOCK;
};
int	 _World::determineBlockGraphicByName(char* Name)
{
	if(Name == "Foam")				return DIRT_ASSET_INDEX;
	else if(Name == "Turf")			return GRASS_ASSET_INDEX;
	else if(Name == "Scrap")		return ROCK_ASSET_INDEX;
	else if(Name == "Fiber")		return TREE_ASSET_INDEX;
	else if(Name == "Algae Wire")	return LEAF_ASSET_INDEX;
	else if(Name == "Inorite")		return ORE1_ASSET_INDEX;
	return LEAF_ASSET_INDEX;
}

//======COLLISION CHECKS
bool const		_World::boolPointCollide(Vector2D toCheck)
{
	Block_Info* block;
	Quad		blockRect;

	for(int i = 0; i < CollideListSize; i++)
	{
		block = CollideList[i];
		if(block && block->Data->GetType_s() > BLOCK_COLLIDABLE ) 
		{
			blockRect.x		= block->X			- short(BLOCK_TEXTURE_SIZE * 0.5);
			blockRect.y		= (block->Y * -1)	- short(BLOCK_TEXTURE_SIZE * 0.5);
			blockRect.width	= BLOCK_TEXTURE_SIZE;
			blockRect.height= BLOCK_TEXTURE_SIZE;

			if (block->Data->GetType_s() > BLOCK_COLLIDABLE && blockRect.isColliding(toCheck)) 
				return true;
		}
	}
	return false;
};
RetVal const	_World::dataBlockCollide(pointQuad toCheck) 
{
	Block_Info* block;
	Quad		blockRect;
	RetVal		collideData, collectedData;

	for(int i = 0; i < CollideListSize; i++)
	{
		block = CollideList[i];
		if(block) 
		{
			blockRect.x		= block->X			- short(BLOCK_TEXTURE_SIZE * 0.5);
			blockRect.y		= (block->Y * -1)	- short(BLOCK_TEXTURE_SIZE * 0.5);
			blockRect.width	= BLOCK_TEXTURE_SIZE;
			blockRect.height= BLOCK_TEXTURE_SIZE;

			if (block->Data->GetType_s() > BLOCK_COLLIDABLE && blockRect.isColliding(toCheck)) 
			{
				if(blockRect.getMinX() <= toCheck.getMaxX())
				{
					collectedData.x		= true;
					collectedData.xObj	= (void*)block;
					collectedData.xP	= block->X;
					collectedData.yP	= block->Y;
				}
				if(blockRect.getMinY() <= toCheck.getMaxY()	||	blockRect.getMaxY() >= toCheck.getMinY())
				{
					collectedData.y		= true;
					collectedData.yObj	= (void*)block;
				}
			}
			collideData.Add(collectedData);
			collectedData.clear();
			if(collideData.x || collideData.y)
				return collideData;
		}
	}
	return collideData;
}
bool const		_World::boolBlockCollide(pointQuad toCheck){
	Block_Info* block;
	Quad		blockRect;

	for(int i = 0; i < CollideListSize; i++)
	{
		block = CollideList[i];
		if(block) 
		{
			blockRect.x		= block->X			- short(BLOCK_TEXTURE_SIZE * 0.5);
			blockRect.y		= (block->Y * -1)	- short(BLOCK_TEXTURE_SIZE * 0.5);
			blockRect.width	= BLOCK_TEXTURE_SIZE;
			blockRect.height= BLOCK_TEXTURE_SIZE;

			if (block->Data->GetType_s() > BLOCK_COLLIDABLE && blockRect.isColliding(toCheck)) 
				return true;
		}
	}
	return false;
}
void			_World::ProjectileCollide(Entity * E, pointQuad ERect) 
{
	Quad ProjQuad;
	for(int i = 0; i < ProjectileCollideListSize ; i++) 
	{
		Item_Projectile * toCollide;
		toCollide = ProjectileCollideList[i];

		//Build projectile rect 
		ProjQuad.set(short(toCollide->getPos().x),short(toCollide->getPos().y * -1), 10, 10);
		if(ProjQuad.isColliding(ERect) && toCollide->movementOn)
		{
			if(toCollide->active && E->TakeDamage(toCollide)) {
				Factory::get().uncreate((Item_Base*)(toCollide),ITEM_PROJECTILE);
				if(E->getEntType() == P_TYPE_PLAYER && !E->isAlive()) {
					ResetPlayer(E,true);
				}
				else if(E->getEntType() == P_TYPE_ENEMY) {
					
					// send message to network that entity has taken damage
					short* data = new short[4];
					data[0] = E->uniqueID;
					data[1] = E->getPierceDmg();
					data[2] = E->getImpactDmg();
					data[3] = E->getEnergyDmg();

					Engine::Ins()->game.m_network.MakeServerRequest(NETWORK_UPDATE_ENTITY_HEALTH, data, sizeof(short)*4);
					delete data;

					// add score to player that killed it
					if(!E->isAlive())
						Players[LOCAL_PLAYER]->PC_handleScore(1); //Killed an enemy, give the player a point
				}
			}
		}
	}
}
